package com.ikingtech.platform.service.office.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.googlecode.aviator.AviatorEvaluator;
import com.googlecode.aviator.Expression;
import com.ikingtech.framework.sdk.context.exception.FrameworkException;
import com.ikingtech.framework.sdk.core.response.R;
import com.ikingtech.framework.sdk.office.model.dto.OfficeDataCustomBranchDto;
import com.ikingtech.framework.sdk.office.model.dto.OfficeDataCustomBranchLogicDto;
import com.ikingtech.framework.sdk.office.model.dto.OfficeDataCustomFieldsDto;
import com.ikingtech.framework.sdk.office.model.dto.OfficeDataCustomFieldsParamsDto;
import com.ikingtech.framework.sdk.office.model.enums.ConditionsEnum;
import com.ikingtech.framework.sdk.office.model.enums.OfficeDataEnums;
import com.ikingtech.framework.sdk.office.model.vo.OfficeDataCustomFieldsVo;
import com.ikingtech.framework.sdk.utils.Tools;
import com.ikingtech.platform.service.office.entity.OfficeDataCustomBranch;
import com.ikingtech.platform.service.office.entity.OfficeDataCustomBranchLogic;
import com.ikingtech.platform.service.office.entity.OfficeDataCustomFields;
import com.ikingtech.platform.service.office.entity.OfficeDataCustomFieldsParams;
import com.ikingtech.platform.service.office.mapper.OfficeDataCustomBranchLogicMapper;
import com.ikingtech.platform.service.office.mapper.OfficeDataCustomFieldsMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * office-自定义字段 服务实现类
 * </p>
 *
 * @author lqb
 * @since 2023-10-13 09:28:43
 */
@Slf4j
@RequiredArgsConstructor
public class OfficeDataCustomFieldsService extends ServiceImpl<OfficeDataCustomFieldsMapper, OfficeDataCustomFields> {

    private final OfficeDataManagementService officeDataManagementService;
    private final OfficeDataCustomBranchService officeDataCustomBranchService;
    private final OfficeDataCustomFieldsParamsService officeDataCustomFieldsParamsService;
    private final OfficeDataCustomBranchLogicMapper officeDataCustomBranchLogicMapper;

    @Transactional(rollbackFor = Exception.class)
    public R<String> add(OfficeDataCustomFieldsDto dto) {
        // 初始化值
        OfficeDataCustomFields fields = dto.copy(new OfficeDataCustomFields(), "branchList", "params");
        fields.setFieldKey(Tools.Id.uuid())
                .setSortOrder(this.baseMapper.getMaxSortOrder(dto.getOfficeDataCustomManagementId()))
                .setId(fields.getFieldKey());

        // 参数转换
        Map<String, String> params = this.convertToMap(dto.getParams());
        // 编写解析器逻辑代码
        String aviatorScript = this.generateAviatorScript(dto.getBranchList(), params);
        try {
            // 校验语法是否正确
            AviatorEvaluator.getInstance().validate(aviatorScript);
            // 编译脚本 并放入缓存中
            AviatorEvaluator.getInstance().compile(fields.getId(), aviatorScript, true);
        } catch (Exception e) {
            log.error("自定义参数编译失败", e);
            throw new FrameworkException("自定义参数编译失败");
        }
        // 数据入库
        fields.setAviatorScript(aviatorScript)
                .insert();
        this.officeDataCustomBranchService.addBatch(fields, dto.getBranchList());
        this.officeDataCustomFieldsParamsService.addBatch(fields, dto.getParams());
        return R.ok(fields.getId());
    }


    /**
     * 转换为map
     *
     * @param paramsList 参数列表
     * @return 执行结果
     */
    private Map<String, String> convertToMap(List<OfficeDataCustomFieldsParamsDto> paramsList) {
        Map<String, String> paramsMap = new HashMap<>();
        for (OfficeDataCustomFieldsParamsDto param : paramsList) {
            param.setId(Tools.Id.uuid());
            String key = "[[" + param.getFieldName() + "_" + param.getFieldId();
            if (StringUtils.isBlank(param.getParams())) {
                key += "]]";
            } else {
                key = key + "#" + param.getParams() + "]]";
            }
            paramsMap.put(key, "param_" + param.getId());
        }
        return paramsMap;
    }

    /**
     * 生成脚本
     *
     * @param branchList 分支机构列表
     * @param params     params
     * @return 执行结果
     */
    private String generateAviatorScript(List<OfficeDataCustomBranchDto> branchList, Map<String, String> params) {
        StringBuilder builder = new StringBuilder();
        if (branchList.size() == 0) {
            throw new FrameworkException("默认分支不存在");
        }
        // 只有默认分支
        if (branchList.size() == 1) {
            this.convertLogicExpression(builder, branchList.get(0).getLogicExpression(), params);
        }
        // 分支大于2个
        if (branchList.size() > 1) {
            for (int i = 0; i < branchList.size(); i++) {
                if (i == 0) {
                    // 开始
                    builder.append("if ")
                            .append(this.convertToExpression(branchList.get(i), params))
                            .append("{ \n   return ");
                    this.convertLogicExpression(builder, branchList.get(i).getLogicExpression(), params);
                    builder.append("; \n}");
                } else if (i == branchList.size() - 1) {
                    // 结束
                    builder.append(" else { \n  return ");
                    this.convertLogicExpression(builder, branchList.get(i).getLogicExpression(), params);
                    builder.append("; \n}");
                } else {
                    // 中间
                    builder.append(" elsif ")
                            .append(this.convertToExpression(branchList.get(i), params))
                            .append("{ \n   return ");
                    this.convertLogicExpression(builder, branchList.get(i).getLogicExpression(), params);
                    builder.append("; \n}");
                }
            }
        }
        // 拼接的脚本
        String aviatorScript = builder.toString();
        log.info("\nlogic script: ===> \n{}", aviatorScript);
        // 替换脚本参数
        return this.convertParams(aviatorScript, params);
    }

    /**
     * 转换逻辑表达式
     *
     * @param builder         建设者
     * @param logicExpression 逻辑表达式
     * @param params a
     */
    private void convertLogicExpression(StringBuilder builder, String logicExpression, Map<String, String> params) {
        if (!logicExpression.startsWith("fx(") && !logicExpression.startsWith("[[")) {
            logicExpression = "\"" + logicExpression;
        }
        if (!logicExpression.endsWith(")") && !logicExpression.endsWith("]]")) {
            logicExpression = logicExpression + "\"";
        }
        // 处理函数
        logicExpression = this.convertFx(logicExpression, params, "\" + ", " + \"");
        // 用 + 连接参数 于 字符传
        logicExpression = logicExpression.replace("[[", "\" + [[").replace("]]", "]] + \"");

        // 处理开头
        if (logicExpression.startsWith("\" + ")) {
            logicExpression = logicExpression.substring(3);
        }
        // 处理结尾
        if (logicExpression.endsWith(" + \"")) {
            logicExpression = logicExpression.substring(0, logicExpression.length() - 3);
        }
        builder.append(logicExpression);

    }

    /**
     * 转换参数
     *
     * @param aviatorScript 脚本
     * @param params        params
     * @return 执行结果
     */
    private String convertParams(String aviatorScript, Map<String, String> params) {
        // 处理 fx 函数
        aviatorScript = this.convertFx(aviatorScript, params, "", "");

        // 替换参数 用 + 连接参数于其他字符串
        for (Map.Entry<String, String> entry : params.entrySet()) {
            aviatorScript = aviatorScript.replace(entry.getKey(), entry.getValue());
        }
        log.info("\naviator script: ===> \n{}", aviatorScript);

        return aviatorScript;
    }

    /**
     * 转换 fx 函数
     *
     * @param aviatorScript 飞行员脚本
     * @param params        params
     * @return 执行结果
     */
    private String convertFx(String aviatorScript, Map<String, String> params, String leftJoin, String rightJoin) {
        // 脚本中包含 函数 fx()
        while (aviatorScript.contains("fx(") && aviatorScript.contains(")")) {
            // fx()嵌套深度
            int depth = 0;
            // 找到函数 fx 的开始下标
            int startIndex = aviatorScript.indexOf("fx("), endIndex = aviatorScript.length();
            // 从 （ 开始遍历脚本元素
            for (int i = startIndex + 2; i < endIndex; i++) {
                char c = aviatorScript.charAt(i);
                if ('(' == c) {
                    // 深度 +1
                    depth++;
                } else if (')' == c) {
                    // 深度 -1
                    depth--;
                    // 当深度为0 时 表示函数语句获取完整
                    if (depth == 0) {
                        // 截取被 fx()包裹的函数体
                        String expression = aviatorScript.substring(startIndex + 3, i);
                        // 递归 解决函数嵌套问题
                        expression = this.convertFx(expression, params, leftJoin, rightJoin);
                        // 替换函数中的参数
                        for (Map.Entry<String, String> entry : params.entrySet()) {
                            expression = expression.replace(entry.getKey(), entry.getValue());
                        }
                        // 替换截取的函数部分字符串 fx() 跳出for循环 不包含 fx()
                        aviatorScript = aviatorScript.substring(0, startIndex) + leftJoin + expression + rightJoin + aviatorScript.substring(i + 1);
                        break;
                    }
                }
            }
        }
        return aviatorScript;
    }

    /**
     * 转换为表达式
     *
     * @param branch 树枝
     * @param params a
     * @return 执行结果
     */
    private String convertToExpression(OfficeDataCustomBranchDto branch, Map<String, String> params) {
        List<String> conditions = new ArrayList<>();
        for (OfficeDataCustomBranchLogicDto logic : branch.getLogicList()) {
            String leftParam = this.convertFx(logic.getLeftParam(), params, "", "");
            if (!leftParam.contains("[[") && !leftParam.contains("]]")) {
                leftParam = "\"" + leftParam + "\"";
            }
            String rightParam = this.convertFx(logic.getRightParam(), params, "", "");
            if (!rightParam.contains("[[") && !rightParam.contains("]]")) {
                rightParam = "\"" + rightParam + "\"";
            }
            switch (logic.getLogicOperator()) {
                case EQUAL:
                    conditions.add("logic.eq( " + leftParam + ", " + rightParam + " )");
                    break;
                case NOT_EQUAL:
                    conditions.add("logic.neq( " + leftParam + ", " + rightParam + " )");
                    break;
                case LESS:
                    conditions.add("logic.lt( " + leftParam + ", " + rightParam + " )");
                    break;
                case GREATER:
                    conditions.add("logic.gt( " + leftParam + ", " + rightParam + " )");
                    break;
                case LESS_EQUAL:
                    conditions.add("logic.le( " + leftParam + ", " + rightParam + " )");
                    break;
                case GREATER_EQUAL:
                    conditions.add("logic.ge( " + leftParam + ", " + rightParam + " )");
                    break;
                case IN:
                    conditions.add("include( seq.list( " + rightParam + " ), " + leftParam + " )");
                    break;
                case NOT_IN:
                    conditions.add("!include( seq.list( " + rightParam + " )," + leftParam + " )");
                    break;
            }
        }
        return StringUtils.join(conditions, ConditionsEnum.AND.equals(branch.getConditions()) ? " && " : " || ");
    }


    /**
     * 编辑
     *
     * @param dto 提交参数
     * @return 执行结果
     */
    @Transactional(rollbackFor = Exception.class)
    public R<String> update(OfficeDataCustomFieldsDto dto) {
        // 删除关联数据
        this.deleteRelation(dto.getId());
        // 转换数据类型
        OfficeDataCustomFields fields = dto.copy(new OfficeDataCustomFields(), "branchList", "params");
        // 参数转换
        Map<String, String> params = this.convertToMap(dto.getParams());
        // 编写解析器逻辑代码
        String aviatorScript = this.generateAviatorScript(dto.getBranchList(), params);
        try {
            // 校验语法是否正确
            AviatorEvaluator.getInstance().validate(aviatorScript);
            // 编译脚本 并放入缓存中
            AviatorEvaluator.getInstance().compile(fields.getId(), aviatorScript, true);
        } catch (Exception e) {
            log.error("自定义参数编译失败", e);
            throw new FrameworkException("自定义参数编译失败");
        }
        // 数据入库
        fields.setAviatorScript(aviatorScript)
                .updateById();
        this.officeDataCustomBranchService.addBatch(fields, dto.getBranchList());
        this.officeDataCustomFieldsParamsService.addBatch(fields, dto.getParams());
        return R.ok(fields.getId());
    }

    /**
     * 详情
     *
     * @param id id
     * @return 执行结果
     */
    public OfficeDataCustomFieldsVo detail(String id) {
        OfficeDataCustomFields fields = this.getById(id);
        if (ObjectUtils.isEmpty(fields)) {
            throw new FrameworkException("自定义字段不存在");
        }
        return fields.copy(new OfficeDataCustomFieldsVo())
                .setBranchList(this.officeDataCustomBranchService.queryListByFieldsId(id))
                .setParams(this.officeDataCustomFieldsParamsService.queryListByFieldsId(id));
    }

    /**
     * 删除
     *
     * @param id id
     * @return 执行结果
     */
    @Transactional(rollbackFor = Exception.class)
    public R<String> delete(String id) {
        this.removeById(id);
        this.deleteRelation(id);
        return R.ok(id);
    }

    /**
     * 删除关联数据
     *
     * @param id id
     * @author lqb
     */
    private void deleteRelation(String id) {
        this.officeDataCustomFieldsParamsService.remove(new QueryWrapper<OfficeDataCustomFieldsParams>().lambda()
                .eq(OfficeDataCustomFieldsParams::getOfficeDataCustomFieldsId, id)
        );
        this.officeDataCustomBranchService.remove(new QueryWrapper<OfficeDataCustomBranch>().lambda()
                .eq(OfficeDataCustomBranch::getOfficeDataCustomFieldsId, id)
        );
        this.officeDataCustomBranchLogicMapper.delete(new QueryWrapper<OfficeDataCustomBranchLogic>().lambda()
                .eq(OfficeDataCustomBranchLogic::getOfficeDataCustomFieldsId, id)
        );
        // 删除逻辑引擎脚本缓存
        AviatorEvaluator.getInstance().invalidateCache(id);
    }

    /**
     * 预览
     *
     * @param id     id
     * @param params params
     * @return 执行结果
     */
    public R<Object> preview(String id, Map<String, Object> params) {
        try {
            Expression expression = AviatorEvaluator.getInstance().getCachedExpressionByKey(id);
            if (ObjectUtils.isEmpty(expression)) {
                OfficeDataCustomFields fields = this.baseMapper.selectById(id);
                if (ObjectUtils.isEmpty(fields)) {
                    return R.ok();
                }
                expression = AviatorEvaluator.getInstance().compile(id, fields.getAviatorScript(), true);
            }
            // 执行器参数
            Map<String, Object> envMap = expression.newEnv();
            // 获取自定义分组下的字段 并按 fieldType 分组
            Map<OfficeDataEnums.FieldType, List<OfficeDataCustomFieldsParams>> paramsMap = this.officeDataCustomFieldsParamsService.lambdaQuery()
                    .eq(OfficeDataCustomFieldsParams::getOfficeDataCustomFieldsId, id).list()
                    .stream().collect(Collectors.groupingBy(OfficeDataCustomFieldsParams::getFieldType));
            if (paramsMap.containsKey(OfficeDataEnums.FieldType.BASE)) {
                // 基础参数
                Map<String, List<OfficeDataCustomFieldsParams>> baseParamsMap = paramsMap.get(OfficeDataEnums.FieldType.BASE).stream()
                        .collect(Collectors.groupingBy(OfficeDataCustomFieldsParams::getOfficeDataManagementId));
                for (Map.Entry<String, List<OfficeDataCustomFieldsParams>> entry : baseParamsMap.entrySet()) {
                    // 按params参数分组 没有参数的 赋值为 -1
                    Map<String, List<OfficeDataCustomFieldsParams>> paramsByParamsMap = entry.getValue().stream()
                            .peek(v -> v.setParams(StringUtils.isBlank(v.getParams()) ? "-1" : v.getParams()))
                            .collect(Collectors.groupingBy(OfficeDataCustomFieldsParams::getParams));
                    // 获取参数值
                    for (Map.Entry<String, List<OfficeDataCustomFieldsParams>> paramEntry : paramsByParamsMap.entrySet()) {
                        JSONObject object = JSONUtil.createObj();
                        Object data = this.officeDataManagementService.preview(entry.getKey(), this.queryStringToMap(params, paramEntry.getKey())).getData();
                        if (ObjectUtils.isNotEmpty(data)) {
                            object = JSONUtil.parseObj(data);
                        }

                        for (OfficeDataCustomFieldsParams param : paramEntry.getValue()) {
                            envMap.put("param_" + param.getId(), object.get(param.getFieldKey()));
                        }
                    }
                }
            }
            if (paramsMap.containsKey(OfficeDataEnums.FieldType.CUSTOM)) {
                // 自定义参数
                for (OfficeDataCustomFieldsParams param : paramsMap.get(OfficeDataEnums.FieldType.CUSTOM)) {
                    Object obj = this.preview(param.getFieldId(), params).getData();
                    envMap.put("param_" + param.getId(), obj);
                }
            }
            // 执行逻辑引擎
            return R.ok(expression.execute(envMap));
        } catch (Exception e) {
            log.error(e.getMessage());
            e.printStackTrace();
            return R.failed();
        }
    }

    /**
     * 参数字符串转 map
     *
     * @param params      params
     * @param queryString 查询参数一串
     * @return 执行结果
     */
    private Map<String, Object> queryStringToMap(Map<String, Object> params, String queryString) {
        if ("-1".equals(queryString)) {
            return params;
        }
        Map<String, Object> map = new HashMap<>(params);
        String[] pairs = queryString.split("&");
        for (String pair : pairs) {
            int idx = pair.indexOf("=");
            if (idx > 0) {
                String key = pair.substring(0, idx);
                String value = pair.substring(idx + 1);
                if (isValidKey(key) && isValidValue(value)) {
                    if (value.indexOf(",") > 0) {
                        map.put(key, Arrays.asList(value.split(",")));
                    } else {
                        map.put(key, value);
                    }
                }
            }
        }
        return map;
    }

    /**
     * key 是否有效
     *
     * @param key 钥匙
     * @return boolean
     */
    private boolean isValidKey(String key) {
        return key != null && !key.isEmpty();
    }

    /**
     * value 是否有效
     *
     * @param value 价值
     * @return boolean
     */
    private boolean isValidValue(String value) {
        return value != null && !value.isEmpty();
    }

    /**
     * 复制
     *
     * @param id 字段
     * @return 执行结果
     */
    @Transactional(rollbackFor = Exception.class)
    public R<String> copy(String id) {
        OfficeDataCustomFieldsVo detail = this.detail(id);
        if (ObjectUtils.isEmpty(detail)) {
            throw new FrameworkException("目标字段不存在");
        }

        OfficeDataCustomFieldsDto dto = BeanUtil.copyProperties(detail, OfficeDataCustomFieldsDto.class);
        dto.setFieldName(detail.getFieldName() + "(副本)")
                .setId(Tools.Id.uuid());
        return this.add(dto);
    }
}

